#include <errno.h>
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>

#include <aura.h>
#include <auraplus.h>
#include <linknode.h>

#include "thread.h"

EXTERN_C Aura_ECode MaskSignal(sigset_t *pSigNewMask, sigset_t *pSigOldMask);
EXTERN_C void UnmaskSignal(sigset_t *pSigOldMask);
EXTERN_C Aura_ECode SetMilliTimer(Aura_Millisecond time);
EXTERN_C Aura_ECode SetTimeOut(Aura_Millisecond timeOut, Aura_WaitResult * pResult);

struct ConditionInfo : public DLinkNode {
    Aura_UInt32       m_uMagicnumberStart;
    Aura_Handle       m_hLock;
    DLinkNode    m_dWaitThreadList;
    Aura_UInt32       m_uMagicnumberEnd;

    ConditionInfo()
        {
            m_uMagicnumberStart = MAGIC_START_NUMBER;
            m_hLock = AURA_NULL;
            m_dWaitThreadList.Initialize();
            m_uMagicnumberEnd = MAGIC_END_NUMBER;
        }
};

AURA_API AuraCondition_Create(
        /* [in] */ Aura_Handle * phCondition)
{
    ConditionInfo *pConditionInfo;
    Aura_ECode ec;

    pConditionInfo = new ConditionInfo();
    if(AURA_NULL == pConditionInfo){
        PERROR("Error:Create Condition failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_OUT_OF_MEMORY;
    }

    ec = CreateLock(&pConditionInfo->m_hLock);
    if(AURA_FAILED(ec)){
        PERROR("Error:Create Condition failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return ec;
    }

    if(AURA_NULL != phCondition){
        *phCondition = (Aura_Handle)pConditionInfo;
    }

    return AURA_NOERROR;

}

AURA_API AuraCondition_Wait(
        /* [in] */ Aura_Handle hCondition,
        /* [in] */ Aura_Handle hMutex,
        /* [in] */ Aura_Millisecond timeOut,
        /* [out] */ Aura_WaitResult * pResult)
{
    Aura_ECode ec = AURA_NOERROR;
    int ret;
    ConditionInfo *pConditionInfo;
    ThreadInfo *pThreadInfo;
    ThreadRef *pThreadRef;

    sigset_t  SigZeroMask;
    sigset_t  SigNewMask;
    sigset_t  SigOldMask;

    pConditionInfo = (ConditionInfo *)hCondition;

    if((AURA_NULL == pConditionInfo)
        || (!IsValidHandle(ConditionInfo, pConditionInfo))){
        PERROR("Error: Condition Wait failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    sigemptyset(&SigZeroMask);
    sigemptyset(&SigNewMask);
    sigemptyset(&SigOldMask);

    pThreadInfo = GetCurrentThreadInfo();

    if(AURA_NULL != pResult){
        *pResult = Aura_WaitResult_OK;
    }

    ec = MaskSignal(&SigNewMask, &SigOldMask);
    if(AURA_FAILED(ec)){
        PERROR("Error: Condition Wait failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return ec;
    }

    //Has interrupted
    if(INTERRUPT & pThreadInfo->m_uState){
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_Interrupted;
        }
        pThreadInfo->m_uState &= ~INTERRUPT;
        goto EXIT;
    }

    //Set Timer
    ec = SetTimeOut(timeOut, pResult);
    if(AURA_FAILED(ec)){
        goto EXIT;
    }

    //Add thread info to event
    Lock(pConditionInfo->m_hLock);

    pThreadRef = new ThreadRef(pThreadInfo);
    if(AURA_NULL == pThreadRef){
        PERROR("Error:Condition Wait failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        ec = AURA_E_OUT_OF_MEMORY;
        goto EXIT;
    }

    pConditionInfo->m_dWaitThreadList.InsertPrev(pThreadRef);
    pThreadInfo->m_uState |= WAITING;

    Unlock(pConditionInfo->m_hLock);

    AuraMutex_Unlock(hMutex);

    //Wait
    do{
        ret = sigsuspend(&SigZeroMask);
        if((ret == -1)&&(EINTR != errno)){
            PERROR("Error:Condition Wait failed! \
            file:%s. line:%d. \n",
            __FILE__, __LINE__);
            ec = AURA_E_INVALID_OPERATION;
            goto CLEAN_EXIT;
        }
    }while(pThreadInfo->m_uState & WAITING);

    if(INTERRUPT & pThreadInfo->m_uState){
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_Interrupted;
        }
        goto CLEAN_EXIT;
    }

    if(TIMEOUT & pThreadInfo->m_uState){
        if(AURA_NULL != pResult){
            *pResult = Aura_WaitResult_TimedOut;
        }
        goto CLEAN_EXIT;
    }

CLEAN_EXIT:

    //enter the conditon info and clean the threadinfo
    Lock(pConditionInfo->m_hLock);
    if(!pThreadRef->IsEmpty()){
        pThreadRef->Detach();
    }
    Unlock(pConditionInfo->m_hLock);

    //Only here delete the threadinfo
    delete pThreadRef;

    AuraMutex_Lock(hMutex, AURA_INFINITE, AURA_NULL);


EXIT:
    pThreadInfo->m_uState &= ~INTERRUPT;
    pThreadInfo->m_uState &= ~TIMEOUT;
    UnmaskSignal(&SigOldMask);
    return ec;
}


AURA_API AuraCondition_Pulse(
        /* [in] */ Aura_Handle hCondition)
{
    ThreadInfo *pThreadInfo;
    ThreadRef  *pThreadRef;
    ConditionInfo *pConditionInfo;
    pConditionInfo = (ConditionInfo *)hCondition;

    if((AURA_NULL == pConditionInfo)
        || (!IsValidHandle(ConditionInfo, pConditionInfo))){
        PERROR("Error: Condition Pulse failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    Lock(pConditionInfo->m_hLock);

    //No Thread waiting for
    if(pConditionInfo->m_dWaitThreadList.IsEmpty()){
        Unlock(pConditionInfo->m_hLock);
        return AURA_NOERROR;
    }

    //pulse a thread
    pThreadRef  = (ThreadRef *)pConditionInfo->m_dWaitThreadList.Next();
    pThreadInfo = pThreadRef->m_pThreadInfo;
    pThreadRef->Detach();
    pThreadRef->Initialize();

    kill((pid_t)pThreadInfo->m_iThreadId, SIGUSR1);

    Unlock(pConditionInfo->m_hLock);

    return AURA_NOERROR;
}

AURA_API AuraCondition_PulseAll(
        /* [in] */ Aura_Handle hCondition)
{
    ThreadInfo *pThreadInfo;
    ThreadRef  *pThreadRef;
    ConditionInfo *pConditionInfo;
    pConditionInfo = (ConditionInfo *)hCondition;

    if((AURA_NULL == pConditionInfo)
        || (!IsValidHandle(ConditionInfo, pConditionInfo))){
        PERROR("Error: Condition Pulse  All failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    Lock(pConditionInfo->m_hLock);

    //No Thread waiting for
    if(pConditionInfo->m_dWaitThreadList.IsEmpty()){
        Unlock(pConditionInfo->m_hLock);
        return AURA_NOERROR;
    }

    //pulse all thread
    while(&pConditionInfo->m_dWaitThreadList != pConditionInfo->m_dWaitThreadList.Next()){
        pThreadRef  = (ThreadRef *)pConditionInfo->m_dWaitThreadList.Next();
        pThreadInfo = pThreadRef->m_pThreadInfo;
        pThreadRef->Detach();
        pThreadRef->Initialize();
        kill((pid_t)pThreadInfo->m_iThreadId, SIGUSR1);
    }

    Unlock(pConditionInfo->m_hLock);

    return AURA_NOERROR;
}

//BUGBUG: Make sure no one using the Condition!!!
AURA_API AuraCondition_Destroy(
        /* [in] */ Aura_Handle hCondition)
{
    ConditionInfo *pConditionInfo;
    pConditionInfo = (ConditionInfo *)hCondition;

    if((AURA_NULL == pConditionInfo)
        || (!IsValidHandle(ConditionInfo, pConditionInfo))){
        PERROR("Error: Condition Destroy failed! \
        file:%s. line:%d. \n",
        __FILE__, __LINE__);
        return AURA_E_INVALID_ARGUMENT;
    }

    DestroyLock(pConditionInfo->m_hLock);

    delete pConditionInfo;

    return AURA_NOERROR;
}
